#ifndef _EGPD_H
#define _EGPD_H

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>

#include <arpa/inet.h>
#include <netinet/in.h>

#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>

#if __NetBSD__ || __APPLE__ || __MACH__
#include <limits.h>
#endif 

#include <netdb.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define EGP_VERSION 1
#define MAXLINE 4096
#define MAXPREFIX 1024
#define MAXTERMS 32
#define MAXPREPEND 32
#define MAXROUTES 4
#define MAXPREFIXLIMIT 4
#define MAXPEERS 32
#define MAXPOLICY 8

enum ACTION {
   ACTION_UNDEF = 0,
   ACTION_ACCEPT,
   ACTION_REJECT,
   ACTION_NEXT,
};

enum PEER_TYPE {
   PEER_UNDEF = 0,
   PEER_INT, 
   PEER_EXT,
};

enum NEXTHOP_STATE {
   NEXTHOP_LOOKUP = 0,
   NEXTHOP_CONNECTED,
   NEXTHOP_UNREACH,
   NEXTHOP_REACH
};

enum PEER_STATE {
   PEER_IDLE = 0,
   PEER_AQSN,
   PEER_DOWN,
   PEER_UP,
   PEER_CEASE
};

struct ipaddr {
   union {
      struct in_addr addr4;
      struct in6_addr addr6;
   } address;
   
   u_int16_t prefixlen;
   u_int8_t family;
};

struct nexthop {
   struct ipaddr exithop;
   struct ipaddr truehop;
   enum NEXTHOP_STATE state;
   char *data;
};

struct network {
   struct ipaddr net;
   struct nexthop gateway;
};

struct egpd_stats {
   u_int64_t uptime;
   u_int32_t advt_prefixes;
   u_int32_t advt_total;
   u_int32_t recv_prefixes;
   u_int32_t recv_active;
   u_int32_t recv_total;
   u_int16_t flaps;
};

LIST_HEAD(pfls, prefixlist);
struct prefixlist {
   LIST_ENTRY(prefixlist) entry;
   char *name;
   struct ipaddr list[MAXPREFIX];
   u_int16_t count;
};

TAILQ_HEAD(_rt, rtinfo) routeinfo;
struct rtinfo {
   TAILQ_ENTRY(rtinfo) entry;
   struct route *route;
   struct ipaddr nexthop;
   struct ifnet *rt_if;
   u_int16_t *aspath;
   u_int16_t metric;
   u_int8_t priority;
   u_int8_t tag;
};

struct policy_term {
   char *name;
   char *description;
   struct {
      struct ipaddr route[MAXROUTES];
      struct prefixlist pfl[MAXPREFIXLIMIT];
      u_int8_t aspath;
      u_int8_t rcount;
      u_int8_t pcount;
   } from;
   struct {
      u_int8_t prepend[MAXPREPEND];
      u_int16_t priority;
      enum ACTION action;
   } then;
   u_int8_t hasaction;
};

LIST_HEAD(policies, policy);
struct policy {
   LIST_ENTRY(policy) entry;
   char *name;
   char *description;
   struct policy_term terms[MAXTERMS];
   u_int16_t termcount;
};

struct egpd_peer {
   struct ipaddr ip;
   char *description;
   u_int16_t asnumber;
   struct rtinfo *route;
   enum PEER_STATE state;
   struct egpd_stats stats;
};

LIST_HEAD(egpd_groups, egpd_group);
struct egpd_group {
   LIST_ENTRY(egpd_group) entry;
   char *name;
   char *description;
   struct egpd_peer peers[MAXPEERS];
   struct policy imp[MAXPOLICY];
   struct policy exp[MAXPOLICY];
   u_int16_t localas;
   u_int16_t remoteas;
   enum PEER_TYPE type;
   u_int8_t peercnt;
   u_int8_t impcnt;
   u_int8_t expcnt;
};

struct egpd_config {
   struct egpd_groups groups;
   struct policies policy;
   struct pfls pfl;
   u_int16_t asnumber;
};

__BEGIN_DECLS

int main(int, char *[]);
int parse_config(char *);
void usage(void);

void egp_socket(void);
void get_route(struct egpd_peer);
char *satostring(struct sockaddr *);
void handle_signal(int);

void error(const char *, ...);
void warning(const char *, ...);

void cleanup(void);
void stringfree(char *);
void groupfree(struct egpd_group *);
void prefixlistfree(struct prefixlist *);
void policyfree(struct policy *);

void initvars(void);
struct egpd_group *new_group(void);
struct prefixlist *new_prefixlist(void);
struct policy *new_policy(void);
char *new_string(size_t);

int string2addr(char *, int64_t, struct ipaddr *);
struct prefixlist *get_firstpfl(void);
struct egpd_group *get_firstgroup(void);
struct policy *get_firstpolicy(void);
struct prefixlist *get_pflbyname(char *);
void insert_prefixlist_topolicy(struct prefixlist *);
void insert_route_topolicy(struct ipaddr *);
void insert_aspath_topolicy(u_int16_t);
char *addr2string(struct ipaddr *);
void insert_neighbor_togroup(struct ipaddr *, int);
struct policy *get_policy(const char *string);
int addto_prefixlist(struct ipaddr *);
__END_DECLS

extern char *__progname;
extern int debug;
extern int foreground;
extern struct egpd_config *conf;

#endif 
